/*!	 zstreambuf.h
**	 zstreambuf
**
**	......... ... 2013 Ivan Mahonin
**
**	This package is free software; you can redistribute it and/or
**	modify it under the terms of the GNU General Public License as
**	published by the Free Software Foundation; either version 2 of
**	the License, or (at your option) any later version.
**
**	This package is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
**	General Public License for more details.
**
*/

#ifndef __SYNFIG_ZSTREAMBUF_H
#define __SYNFIG_ZSTREAMBUF_H

#include <streambuf>
#include <istream>
#include <ostream>
#include <vector>
#include <zlib.h>
#include "filesystem.h"

namespace synfig
{

class zstreambuf : public std::streambuf
{
public:
    enum {
        option_bufsize				= 4096,
        option_method				= Z_DEFLATED,
        option_compression_level	= Z_BEST_COMPRESSION,
        option_window_bits			= 16 + MAX_WBITS,
        option_mem_level			= 9,
        option_strategy				= Z_DEFAULT_STRATEGY,

        fast_option_compression_level = Z_BEST_SPEED,
        fast_option_mem_level		= 9,
        fast_option_strategy		= Z_FIXED
    };

private:
    std::streambuf *buf_;

    bool inflate_initialized;
    z_stream inflate_stream_;
    std::vector<char> read_buffer_;

    bool deflate_initialized;
    z_stream deflate_stream_;
    std::vector<char> write_buffer_;

    bool inflate_buf();
    bool deflate_buf(bool flush);

public:
    explicit zstreambuf(std::streambuf *buf);
    virtual ~zstreambuf();

protected:
    virtual int sync();
    virtual int underflow();
    virtual int overflow(int c = EOF);

public:
    static bool pack(std::vector<char> &dest, const void *src, size_t size, bool fast = false);
    static size_t pack(void *dest, size_t dest_size, const void *src, size_t size, bool fast = false);
    static bool unpack(std::vector<char> &dest, const void *src, size_t size);
    static size_t unpack(void *dest, size_t dest_size, const void *src, size_t src_size);
};

class ZReadStream : public FileSystem::ReadStream
{
public:
    typedef etl::handle<ZReadStream> Handle;

private:
    FileSystem::ReadStream::Handle stream_;
    zstreambuf buf_;
    std::istream istream_;

protected:
    virtual size_t internal_read(void *buffer, size_t size)
    {
        return (size_t)istream_.read((char*)buffer, size).gcount();
    }

public:
    ZReadStream(FileSystem::ReadStream::Handle stream):
        FileSystem::ReadStream(stream->file_system()),
        stream_(stream),
        buf_(stream_->rdbuf()),
        istream_(&buf_)
    { }

};

class ZWriteStream : public FileSystem::WriteStream
{
public:
    typedef etl::handle<ZWriteStream> Handle;

private:
    FileSystem::WriteStream::Handle stream_;
    zstreambuf buf_;
    std::ostream ostream_;

protected:
    virtual size_t internal_write(const void *buffer, size_t size)
    {
        for (size_t i = 0; i < size; i++)
            if (!ostream_.put(((const char*)buffer)[i]).good()) {
                return i;
            }

        return size;
    }

public:
    ZWriteStream(FileSystem::WriteStream::Handle stream):
        FileSystem::WriteStream(stream->file_system()),
        stream_(stream),
        buf_(stream_->rdbuf()),
        ostream_(&buf_)
    { }
};
}

#endif